using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using UniRx;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;

public class SignatureManager : SingletonMono<SignatureManager>
{
    public enum PenType
    {
        Pen,
        BrushPen
    }
    
    [Header("UI")]
    public RawImage targetTexture;
    public GameObject hintTextObj;

    [Header("Brush Settings")]
    public Color drawColor = Color.black;
    [Range(8, 25)] public int brushSize = 13;
   

    [Header("Canvas Settings")]
    public int texWidth = 1920;
    public int texHeight = 1080;

    private bool _canDraw = true;
    private Vector2 _lastLocalPos;
    private IDisposable _drawStream;
    private CompositeDisposable _disposables = new CompositeDisposable();

    public  PenType _curPenType = PenType.Pen; 
    private Camera _camera;

    private void OnDestroy()
    {
        _disposables.Dispose();
    }

    //private void Start()
    //{
    //    SetPenType(_curPenType);
    //}

    #region   钢笔签名

    private RenderTexture penRenderTex;
    public Material penMat;

    // 初始化画布
    public void InitPenCanvas()
    {
        texWidth =Screen.width;
        texHeight =Screen.height;

        Debug.Log(texWidth + "  " + texHeight);

        if (penRenderTex != null)
            penRenderTex.Release();

        penRenderTex = new RenderTexture(texWidth, texHeight, 0, RenderTextureFormat.ARGB32);
        penRenderTex.Create();

        if (penMat == null)
            penMat = new Material(Shader.Find("Unlit/Color"));

        ClearCanvas();

        _camera = Camera.main;
        targetTexture.texture = penRenderTex;
    }

    // 绑定UniRx流
    public void BindPenDrawing()
    {
        Debug.Log("mouseUpStream" );

        var mouseUpStream = Observable.EveryUpdate()
            .Where(_ => Input.GetMouseButtonUp(0));

      
        var mouseDownStream = Observable.EveryUpdate()
            .Where(_ => Input.GetMouseButtonDown(0) && _canDraw)
            .Where(_ => IsPointerInSignatureArea(Input.mousePosition));

        _drawStream = mouseDownStream
            .Subscribe(_ =>
            {
                hintTextObj.SetActive(false);

                // 记录起点（本地坐标），并取当前屏幕位置作为 initialPos
                Vector2 initialScreenPos = Input.mousePosition;
                RectTransformUtility.ScreenPointToLocalPointInRectangle(
                    targetTexture.rectTransform,
                    initialScreenPos,
                    _camera,
                    out _lastLocalPos);

                Debug.Log(("记录坐标"));


                // 内部移动流（持续到 mouseUp）
                var moveSub = Observable.EveryUpdate()
                    .Select(__ => Input.mousePosition)
                    .Where(__ => IsPointerInSignatureArea(Input.mousePosition)) 
                    .StartWith(initialScreenPos) // 确保第一对不会和上次连线
                    .Pairwise() // 得到 (previous, current) 对
                    .TakeUntil(mouseUpStream) // 到 mouseUp 时结束该内部流
                    .Subscribe(pair =>
                    {
                        Debug.Log(("逻辑执行了，但是坐标不对！"));
                        // 将屏幕坐标转换为本地坐标（RectTransform 空间）
                        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
                                targetTexture.rectTransform, pair.Previous, _camera, out var last) &&
                            RectTransformUtility.ScreenPointToLocalPointInRectangle(
                                targetTexture.rectTransform, pair.Current, _camera, out var current))
                        {
                            var startTex = LocalToTextureCoord(last);
                            var endTex = LocalToTextureCoord(current);

                            DrawLineGPU(startTex, endTex);
                        }
                    },
                    () => {/* Finish TODO */});

                // 将内部订阅加入 disposables，当外部对象销毁会一起清理
                _disposables.Add(moveSub);
            });

        _disposables.Add(_drawStream);
    }

    // 检查鼠标是否在签名区
    private bool IsPointerInSignatureArea(Vector2 screenPos)
    {
        return RectTransformUtility.RectangleContainsScreenPoint(
            targetTexture.rectTransform,
            screenPos,
            _camera
        );
    }

    private Vector2 LocalToTextureCoord(Vector2 localPoint)
    {
        Rect rect = targetTexture.rectTransform.rect;

    
        float normalizedX = (localPoint.x + rect.width * 0.5f) / rect.width;
        float normalizedY = ((localPoint.y + rect.height * 0.5f) / rect.height);

        float x = normalizedX * penRenderTex.width;
        float y = normalizedY * penRenderTex.height;
     
        return new Vector2(x, y);
    }


    private void DrawLineGPU(Vector2 start, Vector2 end)
    {
        int segments = Mathf.CeilToInt(Vector2.Distance(start, end) / (brushSize * 0.5f));
        for (int i = 0; i <= segments; i++)
        {
            float t = (float)i / segments;
            Vector2 pos = Vector2.Lerp(start, end, t);
            DrawCircleGPU(pos);
        }
    }

    // GPU 画圆点（取代 SetPixel）
    private void DrawCircleGPU(Vector2 texPos)
    {
        float u = texPos.x / texWidth;
        float v = texPos.y / texHeight;

        Graphics.SetRenderTarget(penRenderTex);
        GL.PushMatrix();
        GL.LoadOrtho();

        penMat.SetColor("_Color", drawColor);
        penMat.SetPass(0);

        float radius = (float)brushSize / texWidth;

        GL.Begin(GL.QUADS);
        GL.TexCoord2(0, 0); GL.Vertex3(u - radius, v - radius, 0);
        GL.TexCoord2(1, 0); GL.Vertex3(u + radius, v - radius, 0);
        GL.TexCoord2(1, 1); GL.Vertex3(u + radius, v + radius, 0);
        GL.TexCoord2(0, 1); GL.Vertex3(u - radius, v + radius, 0);
        GL.End();

        GL.PopMatrix();
    }

    // 清空画布
    public void ClearCanvas()
    {
        if (_curPenType == PenType.Pen)
        {
            Graphics.SetRenderTarget(penRenderTex);
            GL.Clear(true, true, Color.clear);
            hintTextObj.SetActive(true);
        }
        else
        {
            Clear(texRender);
        }
    }

    #endregion

    #region 毛笔签名


    private static readonly int MainTex = Shader.PropertyToID("_MainTex");
    private static readonly int Color1 = Shader.PropertyToID("_Color");

    private RenderTexture canvasTexture;
    private readonly List<Vector2> splineBuffer = new List<Vector2>();

    [SerializeField] private float previousBrushSize = 0f;
    [SerializeField] private Vector2 lastPosition;
    [SerializeField] private float lastMoveTime;

    [SerializeField] private int segCount = 40;
    [SerializeField] private Vector2 paintBrushSizeRange = new Vector2(0f, 60f);
    [SerializeField] private float speedFactor = 5;

    public RenderTexture texRender; //画布
    [SerializeField] private Material mat; //给定的shader新建材质
    [SerializeField] private Texture brushTypeTexture; //画笔纹理
    [SerializeField] private Color brushColor = Color.black;
    [SerializeField] private RawImage renderPanel; //RawImage显示

    // 鼠标平滑
    [SerializeField] private float mouseSmoothVelocity = 0.3f;
    private Vector2 _prevMousePoint;
    private Vector2 _prevSmoothMousePoint;
    private Vector2 _prevMouseChangeVector;

    // 毛刺
    [SerializeField] private float targetOffset = 5;

    // 速度平滑
    private List<float> _velocityBuffer = new List<float>();
    private float _smoothVelocity;
    private readonly int velocityBufferSize = 5;

  
    void Update()
    {
        if (_curPenType == PenType.Pen) return;

        if (Input.GetMouseButtonDown(0))
        {
            Vector2 mousePos = Input.mousePosition;
            MouseDown(mousePos);
        }
        else if (Input.GetMouseButton(0))
        {
            Vector2 mousePos = Input.mousePosition;
            MouseMove(mousePos);
        }
        else if (Input.GetMouseButtonUp(0))
        {
            Vector2 mousePos = Input.mousePosition;
            MouseUp(mousePos);
        }

        if (Input.GetKeyDown(KeyCode.C))
        {
            Clear(texRender);
        }
    }

    private void InitPaintCanvas()
    {
        Application.targetFrameRate = 60;
        //texRender = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32);
        //texRender.Create();

        texRender = new RenderTexture(texWidth, texHeight, 0, RenderTextureFormat.ARGB32);
        texRender.Create();

        //texRender = RenderTextureUtil.CreateRenderTextureWithAlpha(Screen.width, Screen.height);
        Clear(texRender);
    }

    void DrawImage()
    {
        renderPanel.texture = texRender;
    }

    private void MouseDown(Vector2 position)
    {
        if (IsPointerInSignatureArea(Input.mousePosition))
        hintTextObj.SetActive(false);

        Vector2 localPos;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            renderPanel.rectTransform, position, renderPanel.canvas.worldCamera, out localPos);

        ResetMouseSmoothing(localPos.x, localPos.y);
        previousBrushSize = 0;
        splineBuffer.Clear();
        splineBuffer.Add(localPos);
        lastPosition = localPos;
        lastMoveTime = Time.time;
        _velocityBuffer.Clear();
    }

    private void MouseMove(Vector2 position)
    {
        Vector2 localPos;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            renderPanel.rectTransform, position, renderPanel.canvas.worldCamera, out localPos);

        if (lastPosition == localPos) return;

        var temp = GetSmoothMousePoint(localPos.x, localPos.y);
        DrawStroke(lastPosition, temp, false);
        lastPosition = temp;
        lastMoveTime = Time.time;
    }

    private void MouseUp(Vector2 position)
    {
        Vector2 localPos;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            renderPanel.rectTransform, position, renderPanel.canvas.worldCamera, out localPos);

        DrawStroke(lastPosition, localPos, true);
        lastPosition = localPos;
        lastMoveTime = Time.time;
    }

    private void ResetMouseSmoothing(float a, float b)
    {
        _prevMousePoint = new Vector2(a, b);
        _prevSmoothMousePoint = new Vector2(a, b);
        _prevMouseChangeVector = Vector2.zero;
    }

    private Vector2 GetSmoothMousePoint(float a, float b)
    {
        Vector2 mouseChange = new Vector2(a - _prevMousePoint.x, b - _prevMousePoint.y);
        Vector2 smoothMousePoint = _prevSmoothMousePoint;

        if (Vector2.Dot(mouseChange, _prevMouseChangeVector) < 0)
            smoothMousePoint = _prevMousePoint;

        _prevMouseChangeVector = mouseChange;
        _prevMousePoint = new Vector2(a, b);

        smoothMousePoint.x += mouseSmoothVelocity * (a - smoothMousePoint.x);
        smoothMousePoint.y += mouseSmoothVelocity * (b - smoothMousePoint.y);
        _prevSmoothMousePoint = smoothMousePoint;

        return smoothMousePoint;
    }

    private void DrawStroke(Vector2 start, Vector2 end, bool brushEnded)
    {
        if (end == Vector2.zero) return;

        splineBuffer.Add(end);
        if (splineBuffer.Count <= 3) return;

        float deltaTime = Time.time - lastMoveTime;
        float distance = Vector2.Distance(start, end);
        float velocity = distance / Mathf.Max(deltaTime * 1000f, 1f);

        _velocityBuffer.Add(velocity);
        if (_velocityBuffer.Count > velocityBufferSize) _velocityBuffer.RemoveAt(0);

        _smoothVelocity = _velocityBuffer.Average();
        float normalizedVelocity = Mathf.Clamp01(_smoothVelocity / speedFactor);
        //float brushSize = Mathf.Lerp(paintBrushSizeRange.y, paintBrushSizeRange.x, normalizedVelocity);

        float pixelScale = renderPanel.rectTransform.rect.width / Screen.width;
        float brushSize = Mathf.Lerp(
            paintBrushSizeRange.y,
            paintBrushSizeRange.x,
            normalizedVelocity) * pixelScale;


        var points = splineBuffer;

        for (int j = 0, m = points.Count - 3; j < m; j++)
        {
            var p0 = points[j];
            var p1 = points[j + 1];
            var p2 = points[j + 2];
            var p3 = points[j + 3];
            var v0 = (p2 - p0) / 2;
            var v1 = (p3 - p1) / 2;

            for (int i = 1, n = segCount + 1; i <= n; i++)
            {
                float seg = (float)i / segCount;
                float tX = (2 * p1.x - 2 * p2.x + v0.x + v1.x) * Mathf.Pow(seg, 3) +
                           (-3 * p1.x + 3 * p2.x - 2 * v0.x - v1.x) * Mathf.Pow(seg, 2) + v0.x * seg + p1.x;
                float tY = (2 * p1.y - 2 * p2.y + v0.y + v1.y) * Mathf.Pow(seg, 3) +
                           (-3 * p1.y + 3 * p2.y - 2 * v0.y - v1.y) * Mathf.Pow(seg, 2) + v0.y * seg + p1.y;

                float interpolatedBrushSize = Mathf.Lerp(previousBrushSize, brushSize, seg);

                float random = UnityEngine.Random.Range(-targetOffset, targetOffset);
                float xSign = UnityEngine.Random.value < 0.5f ? -1 : 1;
                float ySign = UnityEngine.Random.value < 0.5f ? -1 : 1;

                DrawBrush(texRender, tX + xSign * random, tY + ySign * random, interpolatedBrushSize);
            }
        }

        previousBrushSize = brushSize;
        splineBuffer.RemoveAt(0);
    }

    public void Clear(RenderTexture destTexture)
    {
        hintTextObj.SetActive(true);
        Graphics.SetRenderTarget(destTexture);
        GL.PushMatrix();
        GL.Clear(true, true, Color.clear);
        GL.PopMatrix();
    }

    void DrawBrush(RenderTexture destTexture, float x, float y, float scale)
    {
        float w = renderPanel.rectTransform.rect.width;
        float h = renderPanel.rectTransform.rect.height;

        // 将局部坐标转换为0~1的UV
        float u = (x / w) + 0.5f;
        float v = (y / h) + 0.5f;

        Graphics.SetRenderTarget(destTexture);
        GL.PushMatrix();
        GL.LoadOrtho();

        mat.SetTexture(MainTex, brushTypeTexture);
        mat.SetColor(Color1, brushColor);
        mat.SetPass(0);

        float halfSize = (brushTypeTexture.width * scale / Screen.width) * 0.5f;

        GL.Begin(GL.QUADS);
        GL.TexCoord2(0, 0); GL.Vertex3(u - halfSize, v - halfSize, 0);
        GL.TexCoord2(1, 0); GL.Vertex3(u + halfSize, v - halfSize, 0);
        GL.TexCoord2(1, 1); GL.Vertex3(u + halfSize, v + halfSize, 0);
        GL.TexCoord2(0, 1); GL.Vertex3(u - halfSize, v + halfSize, 0);
        GL.End();

        GL.PopMatrix();
    }

    #endregion

    public void SetPenType(PenType penType)
    {
        _curPenType = penType;
        SetIamgeActive();

        if (penType == PenType.Pen)
        {
            StartDrawing();
            _disposables?.Dispose();
            _disposables = new CompositeDisposable();
            InitPenCanvas();
            BindPenDrawing(); 
        }
        else
        {
            StopDrawing();
            _disposables?.Dispose();
            _disposables = new CompositeDisposable();
            InitPaintCanvas();
            DrawImage();
        }
    }

    public void SetIamgeActive()
    {
        var isAcive  = _curPenType == PenType.Pen; 
        targetTexture.gameObject.SetActive(isAcive);
        renderPanel.gameObject.SetActive(!isAcive);
    }

    public Texture2D GetSignatureTexture()
    {
        var rt = _curPenType == PenType.Pen ? penRenderTex : texRender;
       
        var prev = RenderTexture.active;
        RenderTexture.active = rt;
        Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGBA32, false);
        tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0, false);
        tex.Apply(false, false);
        RenderTexture.active = prev;

        return tex; 
    }

    public void SetBrushSize(int size) => brushSize = Mathf.Max(1, size);
    public void SetBrushColor(Color color) => drawColor = color;

    public void StartDrawing() => _canDraw = true;
    public void StopDrawing() => _canDraw = false;
}
